/*************************************************************************
**                                                                      **
**      Y U B I M O N  -  Basic "plain vanilla C" Yubikey monitor app	**
**                                                                      **
**      Copyright 2008 Yubico AB										**
**                                                                      **
**      Date		/ Sig / Rev  / History                              **
**      2008-06-05	/ J E / 0.00 / Main									**
**                                                                      **
*************************************************************************/

#include "yubikey.h"
#include <ykutil.h>
#include <stdio.h>
#include <string.h>

#define	MAX_PAR	8

typedef enum {
    cmd_none, cmd_ambigous, cmd_help, cmd_quit, 
	cmd_status, cmd_kill, cmd_program
} CMD;

static const struct {
    char *opc;
    CMD cmd;
} cmdList[] = {
    "help",     cmd_help,			// Show command list
    "?",        cmd_help,			// Show command list
	"status",	cmd_status,			// Get key status
	"kill",		cmd_kill,			// Kill key
	"program",	cmd_program,		// Program key
	"exit",		cmd_quit,			// Quit monitor
	"quit",		cmd_quit,			// Quit monitor
    0,          cmd_none
};


static void dumpHex(unsigned char *bin, int size)
{
	while (size--) printf(" %02x", *bin++);
}

int main(int argc, char* argv[])
{
	YUBIKEY yk;
	STATUS status;
	CONFIG cfg;
	TICKET tkt;
	int i, j, par[MAX_PAR];
	char buf[128], *p1;
	unsigned char key[16], bin[16];
    CMD cmd = cmd_none;
	unsigned long tstp, ftstp = 0;

	// Initialize libusb

	if (!ykInit()) {
		printf("Failed to initialize libusb. Press enter to quit");
		getchar();
		return 1;
	}

	// Open Yubikey

	if (!(yk = ykOpen())) {
		printf("Cannot open Yubikey. Press enter to quit");
		getchar();
		return 2;
	}

	// Enter monitor

    printf("\nYubikey monitor R0.1 " \
        __DATE__    \
        " "         \
        __TIME__    \
        " - '?' for a list of commands\n"); 

	memset(&key, 0, sizeof(key));

	do {

		// Prompt and get command string

        printf("\n-> ");
		gets(buf);

		// Parse command string 

		for (j = 0, p1 = (char *) buf; *p1 > ' '; j++, p1++) ;
        if (!j) continue;

        for (i = 0, cmd = cmd_none; cmdList[i].opc; i++)
            if (!memcmp(buf, cmdList[i].opc, j)) {
                if (cmd != cmd_none) {
                    cmd = cmd_ambigous;
                    break;
                }
                cmd = cmdList[i].cmd;
            }

        // Retrieve parameters if present 

        for (i = 0; i < MAX_PAR; i++) {
            par[i] = 0;
            for (; *p1 && (*p1 < '0'); p1++) ;
            for (; *p1 >= '0' ; p1++)
                par[i] = (par[i] << 4) | ((*p1 <= '9') ? (*p1 - '0') : ((*p1 - 'A' + 10) & 0xf));
        }

        // Execute command 

        switch (cmd) {

            case cmd_help:
                for (i = 0; p1 = cmdList[i].opc; i++) {
                    if (i) putchar(',');
                    printf(p1);
                }
                break;

			case cmd_quit:
				break;

			case cmd_status:
				if (ykGetStatus(yk, &status, !par[0])) {
					printf("Firmware version %d.%d.%d Touch level %d ", status.versionMajor, 
						status.versionMinor, status.versionBuild, status.touchLevel);
					if (status.pgmSeq)
						printf("Program sequence %d", status.pgmSeq);
					else
						printf("Unconfigured");
				} else
					printf("Get status failed");
				break;

			case cmd_kill:
				printf(ykWriteConfig(yk, 0, 0) ? "Ok" : "Failed");
				break;

			case cmd_program:
				memset(&cfg, 0, sizeof(cfg));
				cfg.tktFlags = TKTFLAG_APPEND_CR;
				printf(ykWriteConfig(yk, &cfg, 0) ? "Ok" : "Failed");
				break;

            case cmd_ambigous:
                printf("Ambigous command");
                break;

            default:

				// It may be an OTP

				if (strlen(buf) < 32) {
					printf("Invalid command");
					break;
				}

				// Parse it

				if (!parseOTP(&tkt, bin, &i, buf, key)) {
					printf("Invalid OTP");
					break;
				}

				// Display result

				printf("Fixed len %d", i);
				if (i) dumpHex(bin, i);

				printf(" UID");
				dumpHex(tkt.uid, sizeof(tkt.uid));

				tstp = tkt.tstpl + ((unsigned long) tkt.tstph << 16UL);
				if (!ftstp) ftstp = tstp;

				printf("\nuseCtr=%d sessionCtr=%d tstp=%lu delta=%lu", tkt.useCtr, tkt.sessionCtr, tstp, tstp - ftstp);

				break;
        }

	} while (cmd != cmd_quit);

	// Close and cleanup

	ykClose(yk);

	return 0;
}

